package com.meituan.sqt.mock.domain.modelB;

import com.meituan.sqt.mock.domain.enums.PayStatusEnum;
import com.meituan.sqt.mock.domain.exception.SqtBizException;
import com.meituan.sqt.mock.infrastructure.persistence.mysql.TypeBRepository;
import com.meituan.sqt.mock.infrastructure.persistence.mysql.po.TypeBOrderInfoPo;
import com.meituan.sqt.mock.infrastructure.persistence.mysql.po.TypeBPayInfoPo;
import com.meituan.sqt.mock.infrastructure.persistence.mysql.po.TypeBRefundInfoPo;
import com.meituan.sqt.request.thirdpay.ThirdPayQueryRequest;
import com.meituan.sqt.request.thirdpay.ThirdPayRequest;
import com.meituan.sqt.request.thirdpay.ThirdRefundRequest;
import com.meituan.sqt.response.thirdpay.ThirdPayQueryRespBody;
import com.meituan.sqt.response.thirdpay.ThirdPayRespBody;
import com.meituan.sqt.response.thirdpay.ThirdRefundRespBody;
import com.meituan.sqt.utils.DateUtil;
import com.meituan.sqt.utils.JsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.List;
import java.util.Objects;

/**
 * @author: chenzhenfeng@meituan.com
 * @date: 2023/2/7 11:31
 * @description:
 */
@Service
@Slf4j
public class TypeBThirdPayService {
    @Resource
    TypeBRepository repository;
    @Resource
    TypeBFactory factory;

    // 仅示例，需要替换为实际的收银台地址
    private static final String THIRD_PAY_URL = "https://www.meituan.com/";

    /**
     * 处理下单请求
     * 客户平台为A类模型时，由于一笔订单，用户只要发起支付，唤起客户平台收银台就会触发一次下单请求，需要根据商企通订单号进行判断
     * @param request 客户平台交易号和收银台页面URL地址
     * @return
     */
    public ThirdPayRespBody pay(ThirdPayRequest request) {
        // 1.幂等处理，根据商企通订单号判断客户系统是否已存在对应订单
        TypeBOrderInfoPo orderInfoPo = repository.queryBySqtBizOrderId(request.getSqtBizOrderId());
        if (Objects.isNull(orderInfoPo)) {
            orderInfoPo = factory.create(request);// 创建订单
            repository.saveOrderInfo(orderInfoPo);
        }
        // 客户平台保证一笔订单只有一笔交易单是支付成功的，如果的确出现一笔订单支付成功多次，客户平台可以进行重复支付退款；商企通查询到一笔订单有多笔tradeNo出现退款时
        // 也会触发重复支付退款流程
        if (orderInfoPo.isPayed()) {
            throw new SqtBizException("订单已存在且已经支付");
        }
        TypeBPayInfoPo payInfoPo = repository.queryByTradeNo(request.getTradeNo());
        if (Objects.isNull(payInfoPo)) {
            payInfoPo = factory.create(request, orderInfoPo.getOrderId());//创建支付单
            repository.savePayInfo(payInfoPo);
        }
        return new ThirdPayRespBody(String.valueOf(payInfoPo.getPayId()), THIRD_PAY_URL);
    }

    /**
     * 处理支付查询请求
     * 注意：即使订单支付成功后发起过退款，支付查询时仍然应该返回支付成功
     * @param request
     * @return
     */
    public ThirdPayQueryRespBody payQuery(ThirdPayQueryRequest request) {
        TypeBPayInfoPo payInfoPo = repository.queryByTradeNo(request.getTradeNo());

        if (Objects.isNull(payInfoPo)) {
            // 不存在该笔交易单记录，可以直接返回支付失败
            ThirdPayQueryRespBody respBody = new ThirdPayQueryRespBody();
            respBody.setTradeNo(request.getTradeNo());
            respBody.setPayStatus(PayStatusEnum.FAILED.getCode());
            return respBody;
        }

        ThirdPayQueryRespBody respBody = new ThirdPayQueryRespBody();
        respBody.setThirdPayOrderId(String.valueOf(payInfoPo.getPayId()));
        respBody.setTradeNo(payInfoPo.getTradeNo());
        respBody.setPayAmount(payInfoPo.getTradeAmount().toString());
        if (Objects.nonNull(payInfoPo.getServiceFeeAmount())) {
            respBody.setServiceFeeAmount(payInfoPo.getServiceFeeAmount().toString());
        }
        respBody.setPayStatus(payInfoPo.getPayStatus());
        if (payInfoPo.isPayed()) { // 返回支付成功时间
            respBody.setPayTime(DateUtil.getStringByDate(payInfoPo.getGmtModified(), null));
        }
        return respBody;
    }

    /**
     * 处理退款请求
     * 注意：
     * 用户支付成功时，是由第三方平台收取用户资金，并通过【支付成功通知接口】通知美团商企通用户完成支付，美团商企通会从第三方平台在美团商企通开通的企业账户扣减资金
     * 用户发起退款时，用户在美团商企通发起退款后，美团商企通校验退款合法后，会将资金退回第三方平台在美团商企通开通的企业账户，然后向第三方平台发起退款请求，因此
     * 需要第三方平台保证内部退款能够处理成功，避免因为第三方平台未正确处理退款导致用户致电美团商企通发起客诉
     * @param request
     * @return
     */
    public ThirdRefundRespBody refund(ThirdRefundRequest request) {
        TypeBPayInfoPo payInfoPo = repository.queryByTradeNo(request.getTradeNo());
        if (Objects.isNull(payInfoPo)) {
            throw new SqtBizException("支付记录不存在，不允许发起退款，tradeNo:" + request.getTradeNo());
        }
        if (!payInfoPo.isPayed()) {
            throw new SqtBizException("订单未支付，不允许发起退款，tradeNo:" + request.getTradeNo());
        }

        // 【幂等处理】
        TypeBRefundInfoPo refundInfoPo = repository.queryByRefundTradeNo(request.getRefundTradeNo());
        if (Objects.nonNull(refundInfoPo)) {
            log.info("退款申请已经存在，不能重复申请，请求参数：{}", JsonUtil.object2Json(request));
            return new ThirdRefundRespBody(String.valueOf(refundInfoPo.getRefundId()));
        }

        // 【金额校验】一笔订单可以发起多次退款，需要校验总金额，已退款金额+本次退款金额不允许超过支付金额
        List<TypeBRefundInfoPo> refundInfoPos = repository.listRefundInfosByPayId(payInfoPo.getPayId());
        BigDecimal totalRefundAmount = refundInfoPos.stream().map(TypeBRefundInfoPo::getRefundAmount)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        if (totalRefundAmount.add(new BigDecimal(request.getRefundAmount())).compareTo(payInfoPo.getTradeAmount()) > 0) {
            throw new SqtBizException("退款总额超过支付金额，不允许发起退款，tradeNo:" + request.getTradeNo()
                    + "，已退款金额:" + totalRefundAmount + ", 本次退款金额:" + request.getRefundAmount() + ", 支付金额：" + payInfoPo.getTradeAmount());
        }

        //【异步处理退款】 第三方平台可能涉及较复杂耗时的退款处理流程，建议第三方平台接收退款请求后先保存，内部再异步处理退款
        refundInfoPo = factory.create(request, payInfoPo);

        repository.saveRefundInfo(refundInfoPo);

        return new ThirdRefundRespBody(String.valueOf(refundInfoPo.getRefundId()));
    }
}
